// fast4j - Fast & Agile Service Tools for Java
// Copyright (C) 2007 Alexandre ROMAN
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

// $Id: ServiceBridgeBean.java 27 2007-03-26 19:48:20Z alexandre.roman $

package fast4j.ejbbridge.ejb;

import java.util.Map;

import javax.ejb.CreateException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.ejb.support.AbstractStatelessSessionBean;

import fast4j.ejbbridge.ServiceBridge;
import fast4j.ejbbridge.ServiceBridgeContextHolder;
import fast4j.ejbbridge.ServiceBridgeException;
import fast4j.ejbbridge.ServiceCall;
import fast4j.ejbbridge.ServiceResult;

/**
 * The service bridge provides access to Spring services through an EJB facade.
 * @ejb.bean display-name="Service Bridge"
 *           jndi-name="ejb/fast4j.ejbbridge.ejb.ServiceBridgeHomeRemote"
 *           local-jndi-name="ejb/fast4j.ejbbridge.ejb.ServiceBridgeHomeLocal"
 * @ejb.home remote-class="fast4j.ejbbridge.ejb.ServiceBridgeHomeRemote"
 *           local-class="fast4j.ejbbridge.ejb.ServiceBridgeHomeLocal"
 * @ejb.interface remote-class="fast4j.ejbbridge.ejb.ServiceBridgeRemote"
 *                local-class="fast4j.ejbbridge.ejb.ServiceBridgeLocal"
 * @jonas.bean ejb-name="ServiceBridge"
 *             jndi-name="ejb/fast4j.ejbbridge.ejb.ServiceBridgeHomeRemote"
 * @author alexandre.roman
 */
public class ServiceBridgeBean extends AbstractStatelessSessionBean implements
        SessionBean, ServiceBridge {
    private static final long serialVersionUID = 1L;
    private final transient Logger log = LoggerFactory.getLogger(getClass());
    private ServiceInvoker serviceInvoker;
    
    protected void onEjbCreate() throws CreateException {
        if (!(getBeanFactory() instanceof ListableBeanFactory)) {
            throw new CreateException(
                    "Failed to create ServiceBridge: the Spring context must implement "
                            + ListableBeanFactory.class.getName());
        }
        serviceInvoker = new ServiceInvoker();
        log.info("ServiceBridge is ready!");
    }
    
    protected void onEjbRemove() {
        serviceInvoker = null;
    }
    
    public void setSessionContext(SessionContext sessionContext) {
        super.setSessionContext(sessionContext);
        // this class loads a single BeanFactory
        // TODO make Spring context classpath and key locator configurable
        setBeanFactoryLocator(ContextSingletonBeanFactoryLocator
                .getInstance("classpath*:META-INF/spring.xml"));
        setBeanFactoryLocatorKey("context");
    }
    
    /**
     * {@link ServiceBridge#call(ServiceCall)} implementation.
     * @ejb.interface-method view-type="both"
     * @ejb.transaction type="Required"
     */
    public ServiceResult call(ServiceCall call) throws ServiceBridgeException {
        // copy context
        final Map context = ServiceBridgeContextHolder.get();
        context.putAll(call.getContext());
        
        log.debug("Service call BEFORE invocation: {}", call);
        
        try {
            // return result
            final ListableBeanFactory beanFactory = (ListableBeanFactory) getBeanFactory();
            return new ServiceResult(serviceInvoker.invoke(beanFactory, call,
                context), context);
        } finally {
            // copy back context
            // (the context call is referenced by a ServiceResult or an
            // exception being thrown: updating it ensures values will go back
            // to client)
            call.getContext().putAll(context);
            
            log.debug("Service call AFTER invocation: {}", call);
            
            // clear context
            ServiceBridgeContextHolder.release();
        }
    }
}
